<?php

/*
  Core library script
        
  Author:         Alfredo Mungo
  License:        LGPL
  Copyright:      2012
*/

if(!defined('PHI_ROOTDIR'))
  define('PHI_ROOTDIR', getcwd());

/*
  Model class
*/
class Model {
  function __construct() {}
}
////////////////////////////////////////

/*
  Controller class
*/
class Controller {

  protected $args;
  protected $get;
  protected $post;
    
  function __construct($args) {
    $this->args = new ArgsManager($args);
    $this->get = new GETManager();
    $this->post = new POSTManager();
  }
    
  /*
    Load and display a view.
        
    Arguments:
      string $view: the view name
      array $data: the variables to pass to the view.
      bool $extract: true if the $data variable must be extracted (each array element of key K will become a variable called data_K), false if not.
      bool $show: true if the view must immediately be shown.
      
    Return:
      The View class associated with the view $view.
    
    Throws:
      See View::__construct()
      See View::show()
  */
  protected function loadView($view, $data = null, $extract = true, $show = true) {
    
    $v = new View($view);
    $v->setData($data, $extract);
    
    if($show === true)
      $v->show();
    
    return $v;
  }

  /*
    Load a new model.

    Arguments:
      string $model: the model class name to load.

    Return:
      The Model class associated with the model $model

    Throws:
      FileNotFoundException: if the model $model cannot be found in the 'modules/' directory.
  */
  function loadModel($model) {
    $path = PHI_ROOTDIR . "/models/$model.php";

    if(file_exists($path)) {
      include $path;
      $m = new $model();
      return $m;
    } else
      throw new FileNotFoundException($path);
  }
}
///////////////////////////////////////////////

/*
  View class
*/
class View {
  private $view; // View name
  private $path; // View path
  private $data; // View data
  private $data_ext; // Extract data
  
  /*
    Creates a new view
    
    Arguments:
      string $view: The view name (the name of the view as it appears in the 'views' directory, without the '.php' extension).
      
    Throws:
      FileNotFoundException: if the view file cannot be found.
      WrongTypeException: if one of the arguments does not match the required type.
  */
  public function __construct($view) {
  
    if(!is_string($view))
      throw new WrongTypeException();
        
    $this->view = $view;
    $this->path = PHI_ROOTDIR . "/views/$view.php";
    
    if(!file_exists($this->path))
      throw new FileNotFoundException($this->path);
  }
  
  public function setData($data, $extract = true) {
    $this->data = $data;
    $this->data_ext = ($extract === true);
  }
  
  /*
    Loads the view from file and executes it.
    
    Throws:
      WrongTypeException: if setData() has been called with $data not an array and $extract = true.
  */
  public function show() {
    if($this->data_ext && $this->data !== null) {
      if(is_array($this->data))
        extract($this->data, EXTR_PREFIX_ALL, 'data');
      else
        throw new WrongTypeException();
    }
    
    include $this->path;
  }
}
///////////////////////////////////////////////

/*
  HookManager class
*/
class HookManager {

  /*
    IMPORTANT: When adding hooks, remember to set the HOOKS constant to the number of total HOOKS
   */
  const HOOK_DEBUG            = 0; // Run on debug-mode initialization
  const HOOK_RELEASE          = 1; // Run on release-mode initialization
  const HOOK_CTL_BEFORE       = 2; // Run before controller starts
  const HOOK_CTL_AFTER        = 3; // Run after controller finishes
  const HOOK_DIRECTORY        = 4; // Run before listing a directory
  const HOOKS                 = 5; // Number of hooks

  private static $hooks;
    
  public static function init() {
    
    self::$hooks = array();
        
    self::$hooks[self::HOOK_DEBUG] = array();
    self::$hooks[self::HOOK_RELEASE] = array();
    self::$hooks[self::HOOK_CTL_BEFORE] = array();
    self::$hooks[self::HOOK_CTL_AFTER] = array();
    self::$hooks[self::HOOK_DIRECTORY] = array();
  }
    
  public static function run($hook) {
    
    if($hook < self::HOOKS)
      foreach(self::$hooks[$hook] as $handle)
        $handle();
  }
    
  public static function register($hook, $func) {
    
    if($hook < self::HOOKS && is_callable($func)) {
      self::$hooks[$hook][] = $func;
      return true;
    } else return false;
  }

}
///////////////////////////////////////////////////

/*
  ParameterManager class
*/
class ParameterManager {
  protected $data;
    
  function __construct($data) {
    $this->data = new AArray($data);
  }
    
  // Return true if the parameter $name has been defined, false if not
  public function isDefined($name) { return $this->data->hasKey($name); }
    
  /*
    Return the value of the parameter $parm, if defined, throws an exception if not.
        
    THROWS: IndexOutOfBound
  */
  public function getValue($parm) { return $this->data->get($parm); }

  // Return the number of parameters
  public function count() { return $this->data->length(); }
}
///////////////////////////////////////////////////

/*
  Autoload script: registers autoload functions
*/

function autoload_from_folder($folder, $class) {
  $path = PHI_ROOTDIR . "/$folder/" . $class . ".php";
  if(file_exists($path))
    include $path;
}

function autoload_module($class) {
  autoload_from_folder("modules", $class);
}

function autoload_trait($class) {
  autoload_from_folder("core/traits", $class);
}

function autoload_helper($class) {
  autoload_from_folder("helpers", $class);
}

spl_autoload_register('autoload_module');
spl_autoload_register('autoload_helper');
//spl_autoload_register('autoload_trait');
////////////////////////////////////////////////////

/*
  Controller&directory parser script
*/

/*
  Parse the controller URL and return an array containing the controller parameters.
    
  Return value:
  [0] = class name
  [1] = function name
  [...] = parameters
*/
function parse_ctl_url($url) {

  $parms = explode('/', substr($url, strpos($url, $_SERVER['SCRIPT_NAME']) + strlen($_SERVER['SCRIPT_NAME'])));
    
  foreach($parms as $k => $v)
    if(strlen($v) == 0)
      unset($parms[$k]);

  return array_slice($parms, 0);
}

/*
  Parse the directory URL

  Return value:
  string = relative directory path
*/
function parse_dir_url($url) {
  $dir = substr($url, strpos($url, $_SERVER['SCRIPT_NAME']) + strlen($_SERVER['SCRIPT_NAME']) + 1);

  return $dir;
}
//////////////////////////////////////////////////////////

/*
  Exceptions
*/

class IndexOutOfBoundException extends Exception {

  function __construct() {
    parent::__construct("Index out of bound");
  }
}

class UnableToStartSessionException extends Exception {

  function __construct() {
    parent::__construct("Unable to start session");
  }
}

class WrongTypeException extends Exception {

  function __construct() {
    parent::__construct("Wrong type provided");
  }
}

class FileNotFoundException extends Exception {

  function __construct($fname = null) {
    parent::__construct("File " . (is_string($fname)? "'$fname' ": '') . "not found");
  }
}
///////////////////////////////////////////////////////////

/*
  Credentials storage script
*/
if(isset($PHI_CREDENTIALS)) {
  if(count($PHI_CREDENTIALS) > 0)
    foreach($PHI_CREDENTIALS as $cred_name => $cred) {
      $cred = new Credentials($cred[0], $cred[1], $cred[2], (isset($cred[3])? $cred[3]: null));
      Credentials::register($cred_name, $cred);
    }
    
  unset($PHI_CREDENTIALS);
}
    
///////////////////////////////////////////////////////////

?>